﻿using System;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Actya.Core;
using Actya.Core.Content;
using Actya.Core.Infrastructure;
using Actya.Core.SiteStructure;
using Actya.Web.Infrastructure;
using Actya.Web.Mvc.Helpers;
using Actya.Web.ViewModels.Admin;
using MvcPaging;

namespace Actya.Web.Controllers.CmsAdmin
{
	[Authorize(Roles = RoleNames.CmsAdministrators)]
	public class PagesController : Controller
	{
		private const int PageSize = 10;
		private readonly IPageService _pageService;
		private readonly ICmsContext _cmsContext;

		public PagesController(IPageService pageService, ICmsContext cmsContext)
		{
			_pageService = pageService;
			_cmsContext = cmsContext;
		}

		public ActionResult Index(int page = 1)
		{
			int total;
			var pages = this._pageService.GetMostRecentPages(page - 1, PageSize, out total);
			var pagesList = new PagedList<Page>(pages, page - 1, PageSize, total);
			return View(pagesList);
		}

		public ActionResult Create()
		{
			var model = new EditPageViewModel();
			model.UrlPrefix = _cmsContext.Host.Settings.PagesPrefix;
			model.AvailablePageLayouts = _cmsContext.Site.PageLayouts;
			var defaultLayout = model.AvailablePageLayouts.First();
			model.LayoutId = defaultLayout.Id;
			model.ContentModelType = _cmsContext.Site.GetPageTypeById(defaultLayout.PageType).ContentModelType;
			model.Content = CreateContent(model.ContentModelType, defaultLayout);
			return View("Edit", model);
		}

		[HttpPost]
		[ValidateAntiForgeryToken]
		[ValidateInput(false)]
		public ActionResult Create(EditPageViewModel model)
		{
			if (ModelState.IsValid)
			{
				var page = new Page();
				page.Title = model.PageTitle;
				page.Content = GetSanitizedContent(model.Content); // Sanitize content because we don't validate the input to allow raw HTML.
				page.Layout = model.LayoutId;
				page.Slug = model.Slug;
				page.Url = model.Url;
				page.PublicationDate = model.PublicationDate;
				page.OfflineDate = model.OfflineDate;
				page.Sites.Add(this._cmsContext.Site.Id);
				if (!String.IsNullOrEmpty(Request.Form[Constants.SaveAndPublishName]))
				{
					page.Publish();
				}
				try
				{
					_pageService.CreatePage(page, model.IncludeInNavigation, model.ParentId, model.MenuText, model.IsHomePage);
					this.ShowMessage(MessageType.Success, string.Format("The page '{0}' is created successfully.", page.Title), true);
					return RedirectToAction("Update", new { id = page.Id });
				}
				catch (Exception ex)
				{
					this.ShowMessage(MessageType.Error, ex.Message);
				}
			}
			model.AvailablePageLayouts = _cmsContext.Site.PageLayouts;
			return View("Edit", model);
		}

		public ActionResult Update (string id)
		{
			var page = this._pageService.GetPageById(id);
			var model = new EditPageViewModel();
			model.UrlPrefix = _cmsContext.Host.Settings.PagesPrefix;
			model.AvailablePageLayouts = _cmsContext.Site.PageLayouts;
			model.Id = id;
			model.PageTitle = page.Title;
			model.Slug = page.Slug;
			model.Url = page.Url;
			var layout = model.AvailablePageLayouts.First(l => l.Id == page.Layout);
			model.LayoutId = layout.Id;
			model.ContentModelType = _cmsContext.Site.GetPageTypeById(layout.PageType).ContentModelType;
			model.Content = page.Content ?? CreateContent(model.ContentModelType, layout);
			model.IsPublished = page.IsPublished;
			model.PublicationDate = page.PublicationDate;
			model.OfflineDate = page.OfflineDate;

			// Check if page is in sitemap
			var sitemapNode = _cmsContext.Site.FindSitemapNodeByPageId(id);
			if (sitemapNode != null)
			{
				model.IncludeInNavigation = true;
				model.MenuText = sitemapNode.Title;
				model.ParentId = sitemapNode.Parent != null ? sitemapNode.Parent.Id : null;
				model.IsHomePage = sitemapNode.IsHomePage;
			}
			return View("Edit", model);
		}

		[HttpPost]
		[ValidateInput(false)]
		[ValidateAntiForgeryToken]
		public ActionResult Update(EditPageViewModel model)
		{
			if (ModelState.IsValid)
			{
				var page = this._pageService.GetPageById(model.Id);
				page.Title = model.PageTitle;
				page.Slug = model.Slug;
				page.Layout = model.LayoutId;
				page.Content = GetSanitizedContent(model.Content); // Also sanitize
				page.Url = model.Url;
				page.PublicationDate = model.PublicationDate;
				page.OfflineDate = model.OfflineDate;
				var siteId = _cmsContext.Site.Id;
				if (!page.Sites.Contains(siteId))
				{
					page.Sites.Add(this._cmsContext.Site.Id);
				}
				if (!String.IsNullOrEmpty(Request.Form[Constants.SaveAndPublishName]))
				{
					page.Publish();
				}
				try
				{
					_pageService.UpdatePage(page, model.IncludeInNavigation, model.ParentId, model.MenuText, model.IsHomePage);
					this.ShowMessage(MessageType.Success, string.Format("The page '{0}' is updated successfully.", page.Title), true);
					return RedirectToAction("Update", new { id = page.Id });
				}
				catch (Exception ex)
				{
					this.ShowMessage(MessageType.Error, ex.Message);
				}
			}
			model.AvailablePageLayouts = _cmsContext.Site.PageLayouts;
			return View("Edit", model);
		}

		[HttpPost]
		[ValidateAntiForgeryToken]
		public ActionResult UnPublish(string id)
		{
			try
			{
				var page = this._pageService.GetPageById(id);
				this._pageService.UnPublishPage(page);
				this.ShowMessage(MessageType.Success, string.Format("The page '{0}' is unpublished successfully.", page.Title), true);
			}
			catch (Exception ex)
			{
				this.ShowMessage(MessageType.Error, ex.Message, true);
			}
			return RedirectToAction("Update", new { id });
		}

		[HttpPost]
		[ValidateAntiForgeryToken]
		public ActionResult Delete(string id)
		{
			try
			{
				var page = this._pageService.GetPageById(id);
				_pageService.DeletePage(page);
				this.ShowMessage(MessageType.Success, string.Format("The page '{0}' is deleted successfully.", page.Title), true);
			}
			catch (Exception ex)
			{
				this.ShowMessage(MessageType.Error, ex.Message, true);
			}
			return RedirectToAction("Index");
		}

		[HttpPost]
		[ValidateInput(false)]
		[ValidateAntiForgeryToken]
		public ActionResult ChangeLayout(EditPageViewModel model)
		{
			model.AvailablePageLayouts = _cmsContext.Site.PageLayouts;
			var layout = model.AvailablePageLayouts.First(l => l.Id == model.LayoutId);
			model.ContentModelType = _cmsContext.Site.GetPageTypeById(layout.PageType).ContentModelType;
			if (!String.IsNullOrEmpty(model.Id))
			{
				var page = this._pageService.GetPageById(model.Id);
				// We can only re-use page content when the content type stays the same
				if (page.Content != null && Type.GetType(model.ContentModelType).IsAssignableFrom(page.Content.GetType()))
				{
					model.Content = page.Content.SanitizeContent(HtmlSanitizer.sanitize);
				}
				else
				{
					model.Content = CreateContent(model.ContentModelType, layout);
				}
			}
			else
			{
				model.Content = CreateContent(model.ContentModelType, layout);
			}
			return View("Edit", model);
		}

		[HttpPost]
		[ValidateInput(false)]
		[ValidateAntiForgeryToken]
		public ActionResult Preview(EditPageViewModel model)
		{
			if (ModelState.IsValid)
			{
				var page = new Page();
				page.Title = model.PageTitle;
				page.Content = GetSanitizedContent(model.Content); // Sanitize content because we don't validate the input to allow raw HTML.
				page.Layout = model.LayoutId;
				page.Slug = model.Slug;
				_pageService.CreatePreviewPage(page);
				var previewUrl = UrlHelper.GenerateContentUrl(string.Format("~/pagepreview/{0}", page.Url), this.HttpContext);
				return Json(previewUrl);
			}
			throw new HttpException(500, "Unable to preview page. Check if the required fields are entered");
		}

		public ActionResult PagesAutoComplete(string term)
		{
			var pages = this._pageService.SearchPages(term);
			var pagesTransformed = from p in pages
			                       select new
			                              	{
			                              		p.Id, p.Title, p.Url
			                              	};
			var result = Json(pagesTransformed);
			result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
			return result;
		}

		private IContent CreateContent(string contentType, PageLayout layout)
		{
			if (!String.IsNullOrEmpty(contentType))
			{
				// Create content via reflection.
				var content = Activator.CreateInstance(Type.GetType(contentType)) as IContent;
				if (content != null)
				{
					var theme = GetThemeFromLayout(layout);
					content.InitializeForTheme(theme);
				}
				return content;
			}
			return null;
		}

		private Theme GetThemeFromLayout(PageLayout layout)
		{
			var themeName = layout.Theme ?? _cmsContext.Site.DefaultTheme;
			return _cmsContext.Host.GetTheme(themeName);
		}

		private IContent GetSanitizedContent(IContent content)
		{
			return content != null ? content.SanitizeContent(HtmlSanitizer.sanitize) : null;
		}
	}
}
